package com.daotonginfo.generator;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Resource;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Component;

import com.daotonginfo.generator.dao.SchemaDao;
import com.daotonginfo.generator.model.TableInfo;
import com.google.common.base.CaseFormat;

import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;

@Slf4j
@Component
@Data
public class CodeGenerator {
    //JDBC配置，请修改为你项目的实际配置
    public static final String BASE_PACKAGE = "com.daotonginfo";

    private static final String RESOURCES_PATH = "/src/main/resources";//资源文件路径
    private static final String JAVA_PATH = "/src/main/java"; //java文件路径

    private static final String TEMPLATE_FILE_PATH = System.getProperty("user.dir")
            + RESOURCES_PATH
            + "/generator/template";//模板位置

    private static final String AUTHOR = "Zuo jinfang";//@author
    private static final String DATE = new SimpleDateFormat("yyyy/MM/dd").format(new Date());//@date

    private static final String DB_NAME = "medicine_manage";
    private String module = "manage";
    private String projectPath = System.getProperty("user.dir");//项目在硬盘上的基础路径
    private String prefix = "";

    @Resource
    private SchemaDao schemaDao;

    public void genAll(String... tableName) {
        for (String s : tableName) {
            genService(s);
            genModel(s);
            genMapper(s);
            genFront(s);
            genController(s);
        }
    }

    /**
     * 生成路由表等其它信息
     *
     * @param tableNames
     */
    public void genRouter(String... tableNames) {

        try {
            Configuration cfg = getConfiguration();

            Map<String, Object> data = createParams(tableNames);

            File file = new File(
                    projectPath + JAVA_PATH + packageConvertPath(BASE_PACKAGE) + "/" + module + "/front/other.js");
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate("other.js.ftl").process(data, new FileWriter(file));
            log.info("other.js 生成成功");
        } catch (Exception e) {
            e.printStackTrace();
            log.info("other.js 生成失败");
            throw new RuntimeException("生成Service失败", e);
        }
    }

    public void genService(String tableName) {
        String templateName = "service.ftl";
        String service = "service";
        genCode(tableName, templateName, service, "Service.java");
    }

    public void genModel(String tableName) {
        String templateName = "entity.ftl";
        String service = "domain";
        genCode(tableName, templateName, service, "DO.java");

    }

    public void genMapper(String tableName) {
        String templateName = "mapper.ftl";
        String layer = "mapper";
        genCode(tableName, templateName, layer, "Mapper.java");
    }

    public void genController(String tableName) {
        String templateName = "controller.ftl";
        String service = "controller";
        genCode(tableName, templateName, service, "Controller.java");

    }

    public void genFront(String... tableName) {

        String service = "front";
        for (String table : tableName) {
            String templateName = "index.vue.ftl";
            genCode(table, templateName, service, ".vue");
            String tempEdit = "edit.vue.ftl";
            genCode(table, tempEdit, service, "Edit.vue");
        }
    }

    /**
     * 主从表页面的代码生成
     *
     * @param master    　主表名
     * @param slave     　从表名
     * @param parentKey 　主从表之间关联的field
     */
    public void genMasterSlaveFront(String master, String slave, String parentKey) {
        String[] templateNames = { "one2many/master.vue.ftl", "one2many/slave.vue.ftl" };
        String layer = "front";
        Map<String, Object> data = createParams(master, slave, parentKey);
        genCode(master, data, "one2many/master.vue.ftl", layer, ".vue");
        genCode(slave, data, "one2many/slave.vue.ftl", layer, ".vue");
        genCode(master, data, "one2many/masteredit.vue.ftl", layer, "Edit.vue");
        genCode(slave, data, "one2many/slaveedit.vue.ftl", layer, "Edit.vue");
        layer = "service";
        genCode(slave, data, "one2many/service.ftl", layer, "Service.java");
        layer = "controller";
        genCode(slave, data, "one2many/controller.ftl", layer, "Controller.java");

        genService(master);
        genController(master);
        genModel(master);
        genModel(slave);
        genMapper(master);
        genMapper(slave);

    }

    /**
     * 　根据模板和数据生成对应的表的代码
     *
     * @param tableName    　表名
     * @param data         　模板绑定的数据
     * @param templateName 　模板名称
     * @param layer        　层
     * @param suffix       　后缀
     */
    public void genCode(String tableName, Map<String, Object> data, String templateName, String layer, String suffix) {
        TableInfo table = schemaDao.getTable(DB_NAME, tableName, prefix);
        try {
            Configuration cfg = getConfiguration();

            File file = new File(
                    projectPath + JAVA_PATH + packageConvertPath(BASE_PACKAGE) + "/" + module + "/" + layer + "/" +
                            table.getClassName() + suffix);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            cfg.getTemplate(templateName).process(data, new FileWriter(file));
            log.info(file + " 生成成功");

        } catch (Exception e) {
            e.printStackTrace();
            log.info(table.getClassName() + suffix + " 生成失败");
            throw new RuntimeException("生成Service失败", e);
        }
    }

    public void genCode(String tableName, String templateName, String layer, String suffix) {
        Map<String, Object> data = createParams(tableName);
        genCode(tableName, data, templateName, layer, suffix);
    }

    /**
     * 绑定到模板的数据
     *
     * @param tableNames
     * @return
     */
    private Map<String, Object> createParams(String... tableNames) {
        List<TableInfo> list = Stream.of(tableNames)
                .map(table -> schemaDao.getTable(DB_NAME, table, prefix))
                .collect(Collectors.toList());
        Map<String, Object> data = new HashMap<>();
        data.put("date", DATE);
        data.put("author", AUTHOR);
        data.put("basePackage", BASE_PACKAGE);
        data.put("module", module);
        if (tableNames.length == 1) {
            data.put("table", list.get(0));
        }
        data.put("tables", list);
        return data;
    }

    /**
     * 主从表结构的数据
     *
     * @param master
     * @param slave
     * @return
     */
    private Map<String, Object> createParams(String master, String slave, String parentKey) {
        Map<String, Object> data = new HashMap<>();
        data.put("date", DATE);
        data.put("author", AUTHOR);
        data.put("basePackage", BASE_PACKAGE);
        data.put("module", module);
        data.put("parentKey", parentKey);
        data.put("parentKeyCamel", CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, parentKey));
        data.put("master", schemaDao.getTable(DB_NAME, master, prefix));
        data.put("slave", schemaDao.getTable(DB_NAME, slave, prefix));
        return data;
    }

    private static Configuration getConfiguration() throws IOException {
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
        cfg.setDirectoryForTemplateLoading(new File(TEMPLATE_FILE_PATH));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        return cfg;
    }

    private static String packageConvertPath(String packageName) {
        return String.format("/%s/", packageName.contains(".") ? packageName.replaceAll("\\.", "/") : packageName);
    }
}
